package modelo.simulacion;

import java.util.ArrayList;
import modelo.Model;
import modelo.Model.Directions;
import modelo.info.MapPoint;
import agentes.AgentInfoOptionPlace;
import agentes.AgenteConductor;
import agentes.AgentePeaton;


public class TrafficGenerator extends Thread {

	private Model modelo;
	private ArrayList<Point3D> inNorth;
	private ArrayList<Point3D> inSouth;
	private ArrayList<Point3D> inEast;
	private ArrayList<Point3D> inWest;
	private ArrayList<Point3D> outNorth;
	private ArrayList<Point3D> outSouth;
	private ArrayList<Point3D> outEast;
	private ArrayList<Point3D> outWest;
	private Integer selectedPoint;
	
	public static enum Cardinales {norte,sur,este,oeste};
	
	public TrafficGenerator(Model model) {
		
		modelo = model;
		selectedPoint = 0;
		inNorth = new ArrayList<Point3D>();
		inSouth = new ArrayList<Point3D>();
		inEast = new ArrayList<Point3D>();
		inWest = new ArrayList<Point3D>();
		outNorth = new ArrayList<Point3D>();
		outSouth = new ArrayList<Point3D>();
		outEast = new ArrayList<Point3D>();
		outWest = new ArrayList<Point3D>();
	}
	
	public ArrayList<Point3D> getInNorth() {
		return inNorth;
	}

	public void setInNorth(ArrayList<Point3D> north) {
		this.inNorth = north;
	}

	public ArrayList<Point3D> getInSouth() {
		return inSouth;
	}

	public void setInSouth(ArrayList<Point3D> south) {
		this.inSouth = south;
	}

	public ArrayList<Point3D> getInEast() {
		return inEast;
	}

	public void setInEast(ArrayList<Point3D> east) {
		this.inEast = east;
	}
	
	public ArrayList<Point3D> getInWest() {
		return inWest;
	}

	public void setInWest(ArrayList<Point3D> west) {
		this.inWest = west;
	}

	public ArrayList<Point3D> getOutNorth() {
		return outNorth;
	}

	public void setOutNorth(ArrayList<Point3D> outNorth) {
		this.outNorth = outNorth;
	}

	public ArrayList<Point3D> getOutSouth() {
		return outSouth;
	}

	public void setOutSouth(ArrayList<Point3D> outSouth) {
		this.outSouth = outSouth;
	}

	public ArrayList<Point3D> getOutEast() {
		return outEast;
	}

	public void setOutEast(ArrayList<Point3D> outEast) {
		this.outEast = outEast;
	}

	public ArrayList<Point3D> getOutWest() {
		return outWest;
	}

	public void setOutWest(ArrayList<Point3D> outWest) {
		this.outWest = outWest;
	}

	public Integer getSelectedPoint() {
		return selectedPoint;
	}

	public void setSelectedPoint(Integer selectedPoint) {
		this.selectedPoint = selectedPoint;
	}

	public void run() {
		
		getInPoints();
		ArrayList<Point3D> currentPoints = inNorth;
		for (int i=1;i<modelo.getManager().getRunningAgents().size();i++) {
			String tipoAgente = modelo.getManager().getRunningAgents().get(i).getMainClass();
			if (tipoAgente.equals("agentes.AgenteConductor")) {
				AgenteConductor agent = (AgenteConductor) modelo.getManager().getAgentInstance(modelo.getManager().getRunningAgents().get(i).getName());
				System.out.println(agent.getName());
				Point3D punto = getNewInPoint(currentPoints);
				AgentInfoOptionPlace place = getPlace(punto);
				agent.setInitPlace(modelo,place);
				//System.out.println(agent.getInfo().getPerfilTrafico());
			}
			else if (tipoAgente.equals("agentes.AgentePeaton")) {
				AgentePeaton agent = (AgentePeaton) modelo.getManager().getAgentInstance(modelo.getManager().getRunningAgents().get(i).getName());
				System.out.println(agent.getName());
				//System.out.println(agent.getInfo().getPerfilTrafico());
			}
		}
		while (!modelo.getPararSimulacion()) {
			try {
				for (int i=1;i<modelo.getManager().getRunningAgents().size();i++) {
					String tipoAgente = modelo.getManager().getRunningAgents().get(i).getMainClass();
					if (tipoAgente.equals("agentes.AgenteConductor")) {
						AgenteConductor agent = (AgenteConductor) modelo.getManager().getAgentInstance(modelo.getManager().getRunningAgents().get(i).getName());
						System.out.println(agent.getName());
						agent.conducir();
						//System.out.println(agent.getInfo().getPerfilTrafico());
					}
					else if (tipoAgente.equals("agentes.AgentePeaton")) {
						AgentePeaton agent = (AgentePeaton) modelo.getManager().getAgentInstance(modelo.getManager().getRunningAgents().get(i).getName());
						System.out.println(agent.getName());
						agent.andar();
						System.out.println(agent.getInfo().getPerfilTrafico());
					}
				}
				modelo.actualizarContenido(11,modelo.getDriverMap());
				TrafficGenerator.sleep(500-modelo.getSimulationSpeed());
			}
			catch(Exception e) {
				e.printStackTrace();
			}
		}
		modelo.actualizarContenido(11,modelo.getDriverMap());
		finalizar();
	}
	
	public AgentInfoOptionPlace getPlace(Point3D punto) {
	
		String name = modelo.getDriverMap()[punto.x][punto.y].getDataList().get(punto.z).getNombre();
		String type = modelo.getDriverMap()[punto.x][punto.y].getDataList().get(punto.z).getTipo();
		Integer direction = modelo.getDriverMap()[punto.x][punto.y].getDataList().get(punto.z).getSentido();
		AgentInfoOptionPlace place = new AgentInfoOptionPlace(punto.x,punto.y,punto.z,name,type,direction);
		return place;
	}

	public Point3D getNewInPoint(ArrayList<Point3D> currentPoints) {
		
		if (selectedPoint >= currentPoints.size()) {
			selectedPoint = 0;
			if (currentPoints == inNorth)
				currentPoints = inSouth;
			else if (currentPoints == inSouth)
				currentPoints = inWest;
			else if (currentPoints == inWest)
				currentPoints = inEast;
			else if (currentPoints == inEast)
				currentPoints = inNorth;
		}
		Point3D punto = currentPoints.get(selectedPoint);
		selectedPoint = selectedPoint+1;
		return punto;
	}
	
	private void getInPoints() {
		
		MapPoint[][] mapa = modelo.getDriverMap();
		for (int i=0;i<modelo.getMapaPanel().getWidth();i++) {
			Boolean found = false;
			for (int p=0;p<modelo.getDriverMap()[i][0].getDataList().size()&&!found;p++) {
				if (mapa[i][0].getVia() && modelo.adecuadaVehiculos(i,0,p) &&
						comprobarSentido(i,0,p,Directions.Arriba.ordinal()) &&
						comprobarSentido(i,0,p,Directions.ArrDer.ordinal()) &&
						comprobarSentido(i,0,p,Directions.ArrIzq.ordinal()))  {
							found = true;
							Point3D punto = new Point3D(i,0,p,Cardinales.norte.ordinal());
							if (!inNorth.contains(punto)) 
								inNorth.add(punto);
				}
			}
		}	
			
		for (int i=0;i<modelo.getMapaPanel().getWidth();i++) {
			Boolean found = false;
			for (int p=0;p<modelo.getDriverMap()[i][modelo.getMapaPanel().getHeight()-1].getDataList().size()&&!found;p++) {
				if (mapa[i][modelo.getMapaPanel().getHeight()-1].getVia() &&
						modelo.adecuadaVehiculos(i,modelo.getMapaPanel().getHeight()-1,p) &&
						comprobarSentido(i,modelo.getMapaPanel().getHeight()-1,p,Directions.Abajo.ordinal()) &&
						comprobarSentido(i,modelo.getMapaPanel().getHeight()-1,p,Directions.AbDer.ordinal()) && 
						comprobarSentido(i,modelo.getMapaPanel().getHeight()-1,p,Directions.AbIzq.ordinal())) {
							found = true;	
							Point3D punto = new Point3D(i,modelo.getMapaPanel().getHeight()-1,
								p,Cardinales.sur.ordinal());
							if (!inSouth.contains(punto)) 
								inSouth.add(punto);
				}
			}
		}

		for (int j=0;j<modelo.getMapaPanel().getHeight();j++) {
			Boolean found = false;
			for (int p=0;p<modelo.getDriverMap()[0][j].getDataList().size()&&!found;p++) {
				if (mapa[0][j].getVia() && modelo.adecuadaVehiculos(0,j,p) &&
						comprobarSentido(0,j,p,Directions.Izquierda.ordinal()) && 
						comprobarSentido(0,j,p,Directions.ArrIzq.ordinal()) &&
						comprobarSentido(0,j,p,Directions.AbIzq.ordinal())) {
							found = true;
							Point3D punto = new Point3D(0,j,p,Cardinales.oeste.ordinal());
							if (!inWest.contains(punto)) 
								inWest.add(punto);
					}
			}
		}	
		
		for (int j=0;j<modelo.getMapaPanel().getHeight();j++) {
			Boolean found = false;
			for (int p=0;p<modelo.getDriverMap()[modelo.getMapaPanel().getWidth()-1][j].getDataList().size()&&!found;p++) {
				if (mapa[modelo.getMapaPanel().getWidth()-1][j].getVia() &&
						modelo.adecuadaVehiculos(modelo.getMapaPanel().getWidth()-1,j,p) &&
						comprobarSentido(modelo.getMapaPanel().getWidth()-1,j,p,Directions.Derecha.ordinal()) &&
						comprobarSentido(modelo.getMapaPanel().getWidth()-1,j,p,Directions.ArrDer.ordinal()) &&
						comprobarSentido(modelo.getMapaPanel().getWidth()-1,j,p,Directions.AbDer.ordinal())) {
							found = true;
							Point3D punto = new Point3D(modelo.getMapaPanel().getWidth()-1,j,
								p,Cardinales.este.ordinal());
							if (!inEast.contains(punto)) 
									inEast.add(punto);
				}
			}
		}	
	}
	
	public void getOutPoints() {
		
		MapPoint[][] mapa = modelo.getDriverMap();
		for (int i=0;i<modelo.getMapaPanel().getWidth();i++) {
			Boolean found = false;
			for (int p=0;p<modelo.getDriverMap()[i][0].getDataList().size()&&!found;p++) {
				if (mapa[i][0].getVia() && modelo.adecuadaVehiculos(i,0,p) &&
						comprobarSentido(i,0,p,Directions.Abajo.ordinal()) &&
						comprobarSentido(i,0,p,Directions.AbDer.ordinal()) &&
						comprobarSentido(i,0,p,Directions.AbIzq.ordinal()))  {
							found = true;
							Point3D punto = new Point3D(i,0,p,Cardinales.norte.ordinal());
							if (!outNorth.contains(punto)) 
								outNorth.add(punto);
				}
			}
		}	
			
		for (int i=0;i<modelo.getMapaPanel().getWidth();i++) {
			Boolean found = false;
			for (int p=0;p<modelo.getDriverMap()[i][modelo.getMapaPanel().getHeight()-1].getDataList().size()&&!found;p++) {
				if (mapa[i][modelo.getMapaPanel().getHeight()-1].getVia() &&
						modelo.adecuadaVehiculos(i,modelo.getMapaPanel().getHeight()-1,p) &&
						comprobarSentido(i,modelo.getMapaPanel().getHeight()-1,p,Directions.Arriba.ordinal()) &&
						comprobarSentido(i,modelo.getMapaPanel().getHeight()-1,p,Directions.ArrDer.ordinal()) && 
						comprobarSentido(i,modelo.getMapaPanel().getHeight()-1,p,Directions.ArrIzq.ordinal())) {
							found = true;	
							Point3D punto = new Point3D(i,modelo.getMapaPanel().getHeight()-1,
								p,Cardinales.sur.ordinal());
							if (!outSouth.contains(punto)) 
								outSouth.add(punto);
				}
			}
		}

		for (int j=0;j<modelo.getMapaPanel().getHeight();j++) {
			Boolean found = false;
			for (int p=0;p<modelo.getDriverMap()[0][j].getDataList().size()&&!found;p++) {
				if (mapa[0][j].getVia() && modelo.adecuadaVehiculos(0,j,p) &&
						comprobarSentido(0,j,p,Directions.Derecha.ordinal()) &&
						comprobarSentido(0,j,p,Directions.ArrDer.ordinal()) &&
						comprobarSentido(0,j,p,Directions.ArrIzq.ordinal())) {
							found = true;
							Point3D punto = new Point3D(0,j,p,Cardinales.oeste.ordinal());
							if (!outWest.contains(punto)) 
								outWest.add(punto);
					}
			}
		}	
		
		for (int j=0;j<modelo.getMapaPanel().getHeight();j++) {
			Boolean found = false;
			for (int p=0;p<modelo.getDriverMap()[modelo.getMapaPanel().getWidth()-1][j].getDataList().size()&&!found;p++) {
				if (mapa[modelo.getMapaPanel().getWidth()-1][j].getVia() &&
						modelo.adecuadaVehiculos(modelo.getMapaPanel().getWidth()-1,j,p) &&
						comprobarSentido(modelo.getMapaPanel().getWidth()-1,j,p,Directions.Izquierda.ordinal()) &&
						comprobarSentido(modelo.getMapaPanel().getWidth()-1,j,p,Directions.ArrIzq.ordinal()) &&
						comprobarSentido(modelo.getMapaPanel().getWidth()-1,j,p,Directions.AbIzq.ordinal())) {
							found = true;
							Point3D punto = new Point3D(modelo.getMapaPanel().getWidth()-1,j,
								p,Cardinales.este.ordinal());
							if (!outEast.contains(punto)) 
									outEast.add(punto);
				}
			}
		}	
	}
	
	public Boolean comprobarSentido(int x,int y,int p,Integer valor) {
		
		Boolean correcto = false;
		if (modelo.getDriverMap()[x][y].getVia()) {
			if (!modelo.getDriverMap()[x][y].getDataList().get(p).getSentido().equals(valor)) 
				correcto = true;
		}
		return correcto;
	}

	public void finalizar() {
		
		System.out.println("KO gene");
		try {
			this.finalize();
		}
		catch (Throwable t) {
			t.printStackTrace();
		}
	}
}
